#include <sifteo.h>
#include "assets.gen.h"
#include <sifteo/string.h>
using namespace Sifteo;

static const unsigned gNumCubes = 3;
Random gRandom;

static AssetSlot MainSlot = AssetSlot::allocate()
		.bootstrap(BootstrapAssets);

static VideoBuffer vid[gNumCubes];

static Metadata M = Metadata()
    .title("Cube Cube Rocket!")
    .package("com.sifteo.sdk", "1.0")
	.icon(Icon)
    .cubeRange(gNumCubes);

class StarDemo {
	int marbleX = 0;
	int marbleY = 0;
	int marbleVX = 0;
	int marbleVY = 0;
	int cube_id = 0;
	int posX = 0;
	int posY = 0;
	int teleport = 0;
	int dir = 0;
		
public:

	char board[4][4];
	char board2[4][4];

    void init(CubeID cube)
    {
        vid.initMode(BG0_SPR_BG1);
        vid.attach(cube);
        vid.bg0.image(vec(0,0), Background);
        // drawing arrows on the cubes
        if(cube == 0) {
        	vid.bg0.image(vec(0,0), ArrowR);
			board[0][0] = 'r';
        	vid.bg0.image(vec(0,12), ArrowU);
			board[0][3] = 'u';
			vid.bg0.image(vec(12,0), ArrowD);
			board[3][0] = 'd';
			vid.bg0.image(vec(12,12), ArrowL);
			board[3][3] = 'l';

        	vid.sprites[0].setImage(Marble);			
        	cube_id = 0;
        } else if(cube == 1) {
        	vid.bg0.image(vec(12,12),ArrowL);
			board[3][3] = 'l';
        	vid.bg0.image(vec(0,0),ArrowR);
			board[0][0] = 'r';
			vid.bg0.image(vec(0,12), PurpleP);
			board[0][3] = '1';
			vid.bg0.image(vec(12,0), PurpleP);
			board[3][0] = '2';
        	vid.sprites[0].setImage(Marble);
        	//vid.sprites[1].hide();
        } else if(cube == 2) {
			vid.bg0.image(vec(0,0),ArrowR);
			board[0][0] = 'r';
        	vid.bg0.image(vec(4,0),ArrowD);
			board[1][0] = 'd';
        	vid.bg0.image(vec(4,12),ArrowR);
			board[1][3] = 'r';
			vid.bg0.image(vec(12,12),ArrowU);
			board[3][3] = 'u';
			vid.bg0.image(vec(12,8),ArrowL);
			board[3][2] = 'l';
			vid.bg0.image(vec(0,8),ArrowU);
			board[0][2] = 'u';
        	vid.sprites[0].setImage(Marble);
        	//vid.sprites[2].hide();
        }
    }
    
    // pass in the cube which you want to move to
    /*void moveToCube(CubeID cube) {
    	if(cube == 1 && !vid.sprites[0].isHidden() ){
    		
    	} else if(cube == 2 && !vid.sprites[0].isHidden()) {
    	
    	} else if(cube == 3 && !vid.sprites[0].isHidden()) {
    	
    	}
    }*/

	void rotateMarble(char direction) {
		switch(direction){
		case 'r':
			marbleVX = 1;
			marbleVY = 0;
			break;
		case 'l':
			marbleVX = -1;
			marbleVY = 0;
			break;
		case 'u':
			marbleVX = 0;
			marbleVY = -1;
			break;
		case 'd':
			marbleVX = 0;
			marbleVY = 1;
			break;
		case '1':
			/*marbleVX = 0;
			marbleVY = 0;
			vid.sprites[0].move(96,0);*/
			teleport = 1;
			break;
		case '2':
			/*marbleVX = 0;
			marbleVY = 0;
			vid.sprites[0].move(0,96);*/
			teleport = 2;
			break;
		case 'c':
			if(marbleVX > 0) {
				dir = 2;
				marbleX = 32;
				marbleY = 64;
				marbleVX = 0;
				marbleVY = -1;
			} else if(marbleVX < 0) {
				dir = 0;
				marbleX = 32;
				marbleY = 64;
				marbleVX = 0;
				marbleVY = 1;
			} else if(marbleVY > 0) {
				dir = 3;
				marbleX = 32;
				marbleY = 64;
				marbleVY = 0;
				marbleVX = -1;
			} else if(marbleVY < 0) {
				dir = 1;
				marbleX = 32;
				marbleY = 64;
				marbleVY = 0;
				marbleVX = 1;
			}
			break;
		}
	}
    
    void update(TimeDelta timeStep)
    {
    	static StarDemo instances[gNumCubes];
    	
		marbleX += marbleVX;
    	marbleY += marbleVY;

    	for(unsigned i = 0; i < gNumCubes; i++)	{
			if(vid.sprites[0].x()%32 == 0 && vid.sprites[0].y()%32 == 0) {
				posX = vid.sprites[0].x() / 32;
				posY = vid.sprites[0].y() / 32;
				if(board[posX][posY] != '0')
					rotateMarble(board[posX][posY]);
				//if(board2[posX][posY] != '0' && i == 1)
					//teleportMarble(board2[posX][posY]);
			}

			switch(teleport){
			case 0:
				break;
			case 1:
				marbleX = 95;
				marbleY = 0;
				teleport = 0;
				break;
			case 2:
				marbleX = 1;
				marbleY = 96;
				teleport = 0;
				break;
			}
			/*switch(dir) {
			case 0:
				marbleX--;
				break;
			case 1:
				marbleY--;
				break;
			case 2:
				marbleX++;
				break;
			case 3:
				marbleY++;
				break;
			}*/

    		vid.sprites[0].move(vec(marbleX, marbleY));
    	
    		if(vid.sprites[i].x() > 128 && !vid.sprites[i].isHidden()) {
    			//vid.sprites[i].hide();
    			//moveToCube(instances[i]);
    		}
    	}
    	
    	//for (int i = 0; i < arraysize(instances); i++)
    	//	instances[i].moveMarble(i);
    }

private:   
    
    VideoBuffer vid;
};


// CCR.cpp : Defines the entry point for the console application.
//

//#include "stdafx.h"
//#include <vector>


class Obstacle;
class Ball;

//A cube object holds information about the arrangement of the cubes, which will be passed down from the actual hardware stuff
class Cube
{
	public:
		//Cube Id
		int id;

		//Obstacles on this particular cube
		Obstacle* tiles[6];

		Cube(int idParam)
		{
		}
		

private:
		//neighbors[0] is the neighbor to the north, 1 to the east, 2 to the south, and 3 to the west
		//The ints stored are the neighbors' ids
		int neighbors[4];

		//The direction of true north relative to this cube's orientation
		int north;

		//spreads this cube's north to all neighbors
		void propogateNorth(Ball* b)
		{
			//Put unpleasant math here
		}
	
};
//Obstacles are tiles that affect the direction or position of the ball when it passes over them
class Obstacle
{
	public:
		//The cube the obstacle is on
		Cube* cube;

		//The position on the cube
		int posX;
		int posY;
		char type;
	
		//All types of obstacles override this method
		virtual void onContact(Ball* b);

		Obstacle(Cube* c, int x, int y, char t)
		{
			cube = c;
			posX = x;
			posY = y;
			type = t;
		}
};

//A ball is the object that moves through the levels.
//It stores the direction it's currently moving in, relative to true north
//As well as the cube it is in and its current position in that cube.
class Ball
{
public:
	//0 = north, 1 = east, 2 = south, 3 = west;
	int direction;

	Cube* cube;
	int posX;
	int posY;


public:
	//Move increments the position by +/- 1, in either x or y, depending on direction
	void move()
	{
		//If the direction is odd, add to the x position the difference of 2 and the direction (-1 for west, 1 for east)
		posX += direction % 2 == 1 ? 2 - direction : 0;
		posY += direction % 2 == 0 ? 1 - direction : 0;

		
//		for each (Obstacle* o in cube->tiles)
//		{
//			if(o->posX == posX && o->posY == posY)
//				o->onContact(this);
//		}


	}

	Ball(Cube* c, int x, int y)
	{
		cube = c;
		posX = x;
		posY = y;
	}
};
 




//Arrows change the direction of the ball, depending on the direction they themselves point
class Arrow : Obstacle
{
	int direction;
	void onContact(Ball* b)
	{
		b->direction = direction;
	}

	Arrow(Cube* c, int x, int y, int d)
		: Obstacle(c, x, y, 'A')
		{
			direction = d;
		}
};

//Portals transport the ball to the location of their partner
class Portal : Obstacle
{
	Obstacle* partner;
	
	void onContact(Ball* b)
	{
		b->cube = partner->cube; 
		b->posX = partner->posX;
		b->posY = partner->posY;
	}

	Portal(Cube* c, int x, int y)
		: Obstacle(c, x, y, 'P')
		{
		}

	void ConnectPortals(Obstacle* p)
	{
		partner = p;
	}

};

//The ball falls down pits, resetting the level
class Pit : Obstacle
{
	void onContact(Ball* b)
	{
		//reset puzzle
	}

	Pit(Cube* c, int x, int y)
		: Obstacle(c, x, y, 'I')
		{
		}


};

//The ball bounces off of walls, reversing its direction
class Wall : Obstacle
{
	void onContact(Ball* b)
	{
		b->direction = (b->direction + 2) % 4;
		
	}

	Wall(Cube* c, int x, int y)
		: Obstacle(c, x, y, 'W')
		{
		}
};

//Two-state obstacles, as their name implies, have two distinct states
class TwoStateObstacle : Obstacle
{
	public:	
		bool triggered;

		TwoStateObstacle(Cube* c, int x, int y, char t)
		: Obstacle(c, x, y, t)
		{
			triggered = false;
		}


};

//When the ball rolls over a button, both the button and the obstacle it controls are triggered.
class Button : TwoStateObstacle
{
	TwoStateObstacle* partner;	
	void onContact(Ball* b)
	{
		triggered = true;
		partner->triggered = true;
	}

	Button(Cube* c, int x, int y, int d)
		: TwoStateObstacle(c, x, y, 'B')
		{
			triggered = false;
		}
};

//If a gate is untriggered, the ball bounces off it as though it were a wall.
//If it is triggered, it can pass through unhindered
class Gate : TwoStateObstacle
{
	void onContact(Ball* b)
	{
		if(triggered)
		{
			b->direction = (b->direction + 2) % 4;
		}
	}

	Gate(Cube* c, int x, int y, int d)
		: TwoStateObstacle(c, x, y, 'G')
		{
			triggered = false;
		}
};

//Bridges act as pits until they are triggered
class Bridge : TwoStateObstacle
{
	void onContact(Ball* b)
	{
		if(!triggered)
		{
			//reset puzzle
		}
	}

	Bridge(Cube* c, int x, int y, int d)
		: TwoStateObstacle(c, x, y, 'R')
		{
			triggered = false;
		}
};

//The ball reaching the goal results in victory
class Goal : Obstacle
{
	void onContact(Ball* b)
	{
		b->direction = -1;
		//win
	}

	Goal(Cube* c, int x, int y, int d)
		: Obstacle(c, x, y, 'O')
		{
		}
};

//The puzzle class stores an array of 6 cubes and the ball(s)
//It will also be responsible for loading puzzles from xml files, determining north, and other higher level logic
class Puzzle
{
	StarDemo* cubes[6];
	Ball* ball;
	
	

	void xmlLoad()
	{

	}

	void resetLevel()
	{

	}
public:
	Puzzle(StarDemo* cube0, StarDemo* cube1, StarDemo* cube2, StarDemo* cube3, StarDemo* cube4, StarDemo* cube5)
	{
		cubes[0] = cube0;
		cubes[1] = cube1;
		cubes[2] = cube2;
		cubes[3] = cube3;
		cubes[4] = cube4;
		cubes[5] = cube5;
		//ball = new Ball(cubes[0],0,0);
	}

};

/*
void main()
{
    static StarDemo instances[gNumCubes];


    for (unsigned i = 0; i < arraysize(instances); i++)
        instances[i].init(i);
    
    TimeStep ts;
    while (1) {
        for (unsigned i = 0; i < arraysize(instances); i++)
            instances[i].update(ts.delta());

        System::paint();
        ts.next();
    }
}*/

void main()
{
    static StarDemo instances[gNumCubes];

	/*for(unsigned i = 0; i < 4; i++) {
		for(unsigned j = 0; j < 4; j++) {
			StarDemo::board[i][j] = '0';
		}
	}*/

    for (unsigned i = 0; i < arraysize(instances); i++)
        instances[i].init(i);

	//Puzzle* p = new Puzzle(instances[0], instances[1], instances[2], instances[3], instances[4], instances[5]);
    
    TimeStep ts;
    while (1) {
        for (unsigned i = 0; i < arraysize(instances); i++)
            instances[i].update(ts.delta());

        System::paint();
        ts.next();
    }
	//
}